#ifndef PALETTES_HPP
#define PALETTES_HPP
/*
palettes.hpp
psycommando@gmail.com
2014/09/23
Description: A bunch of utilities for dealing with palettes, and color data in general!

#TODO: rename this ! It kinda grew to be a little different than its initial purpose !!
*/
#include "basetypes.hpp"
#include <array>

namespace pmd2 { namespace graphics
{
//==================================================================
// Constants
//==================================================================
    static const uint8_t               RGBX_UNUSED_BYTE_VALUE          = 0x80u; //The value of the unused byte in the 32 bits palette used in PMD2
    static const types::bytevec_szty_t PALETTE_15_BPC_1BPCHAN_AT4PX_SZ = 48; //The length of the 15bits per color, 1 byte per channel, 16 colors palette that come with AT4PX containers in some instances

//==================================================================
// Typedefs
//==================================================================
    class colorRGB24;
    typedef std::vector<graphics::colorRGB24>  rgb24palette_t;

//==================================================================
// Structs
//==================================================================

    /*
        color_container
            A generic class to represent a color made up of several components.
            It also offers facilities to get a RGB24 representation of the color.
    */
    template< class _COLOR_DATA_T, unsigned int _NB_COMPONENTS, class _RGB_24_T = colorRGB24 >
        class color_container
    {
    public:
        typedef _COLOR_DATA_T colordata_t;
        static const unsigned int NB_COMPONENTS = _NB_COMPONENTS;

        color_container() { std::fill( m_colcomponents.begin(), m_colcomponents.end(), 0 ); }
        color_container( const color_container<_COLOR_DATA_T,_NB_COMPONENTS,_RGB_24_T> & other ){ m_colcomponents = other.m_colcomponents; }
        virtual ~color_container(){}

        inline static unsigned int GetNbComponents()                     { return NB_COMPONENTS;          }
        inline colordata_t &       operator[]( unsigned int index )      { return m_colcomponents[index]; }
        inline const colordata_t & operator[]( unsigned int index )const { return m_colcomponents[index]; }
        inline color_container<_COLOR_DATA_T,_NB_COMPONENTS,_RGB_24_T> & operator=( const color_container<_COLOR_DATA_T,_NB_COMPONENTS,_RGB_24_T> & other ) 
        { 
            m_colcomponents = other.m_colcomponents;
            return *this;
        }

        

        //Virtual stuff
        virtual const _RGB_24_T getAsRGB24  ()const                             = 0;
        virtual void            setFromRGB24( uint8_t r, uint8_t g, uint8_t b ) = 0;

        template<class _outit> inline _outit WriteAsRawByte( _outit itwhere, bool blittleendianorder = true )const
        {
            //if( blittleendianorder )
            //{
                for( unsigned int i = 0; i < NB_COMPONENTS; ++i  )
                    itwhere = utils::WriteIntToByteVector( m_colcomponents[i], itwhere, blittleendianorder );
            //}
            //else
            //{
            //    for( unsigned int i = (NB_COMPONENTS-1); i >= 0; --i  )
            //        itwhere = utils::WriteIntToByteVector( m_colcomponents[i], itwhere );
            //}
            return itwhere;
        }

        template<class _init> inline _init ReadAsRawByte( _init itwhere, bool blittleendianorder = true )
        {
            for( unsigned int i = 0; i < NB_COMPONENTS; ++i  )
                m_colcomponents[i] = utils::ReadIntFromByteVector<colordata_t>( itwhere, blittleendianorder );
            return itwhere;
        }

        inline static unsigned int getSizeRawBytes()
        {
            return NB_COMPONENTS * sizeof(colordata_t);
        }

    protected:
        std::array<colordata_t, NB_COMPONENTS> m_colcomponents;
    };


    //=========================================
    // RGB-24bits Format
    //=========================================
    class colorRGB24 : public color_container<uint8_t, 3, colorRGB24>
    {
    public:
        colordata_t & red, & green, & blue; //Aliases

        colorRGB24();
        colorRGB24( colordata_t r, colordata_t g, colordata_t b );
        colorRGB24( const colorRGB24 & other );
        colorRGB24 & operator=( const colorRGB24 & other );
        
        //Overrides
        ~colorRGB24(){}
        const colorRGB24 getAsRGB24  ()const;
        void             setFromRGB24( uint8_t r, uint8_t g, uint8_t b );
    };

    //=========================================
    // RGBA-32bits Format
    //=========================================
    //struct colorRGBA32 : public colorRGB24
    //{
    //    uint8_t alpha;

    //    colorRGBA32();
    //    colorRGBA32( uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255u );

    //    virtual uint8_t& operator[](unsigned int index); //Access bytes as an array
    //};

    //=========================================
    // PMD2 Sprite File Palette
    //=========================================
    /*
        A wrapper class for the data contained in a PMD2 
        Sprite's Palette.
    */
    //#TODO



//==================================================================
// Raw Palettes to RGB24 Converters/Parser
//==================================================================
    /****************************************************************
        rgbx32_parser
            A functor for parsing a raw RGBX 32 bits color palette. 
            As used in most sprites files.
    ****************************************************************/
    class rgbx32_parser
    {
    public:
        //Takes an iterator to the output palette
        rgbx32_parser( std::vector<colorRGB24>::iterator itoutpalette );

        //Takes a byte from the palette raw data
        void operator()( uint8_t abyte );

    private:
        std::vector<colorRGB24>::iterator _curcolor;
        uint8_t                           _curchannel;
    };

    /****************************************************************
        rgb15_parser
            A functor for parsing a raw RGB 15 bits color palette. 
            Those are on 3 bytes, and in each of those, only the 
            first 5 bits are used. At least, in theory.. They're
            mainly used in the kaomado.kao file. 
    ****************************************************************/
    class rgb15_parser
    {
    public:
        //Takes an iterator to the output palette
        rgb15_parser( std::vector<colorRGB24>::iterator itcolor );

        //Takes a byte from the palette raw data
        void operator()( uint8_t abyte );

    private:
        std::vector<colorRGB24>::iterator _curcolor;
        uint8_t                           _curchannel;
    };

    //Read a palette to a container. Range to read must be divisible by colorRGB24::NB_COMPONENTS !
    template<class _init>
        inline _init ReadRawPalette_RGB24_As_RGB24( _init            itbeg,
                                                    _init            itend,
                                                    rgb24palette_t & out_palette )
    {
        colorRGB24   temp;
        unsigned int size = std::distance( itbeg, itend );

        if( size % colorRGB24::NB_COMPONENTS != 0  )
            throw std::length_error("ReadRawPalette_RGB24_As_RGB24() : Nb of bytes to build palette from not divisible by " + std::to_string(colorRGB24::NB_COMPONENTS ));

        out_palette.reserve( size / colorRGB24::NB_COMPONENTS );
        out_palette.resize(0);

        //Write palette
        while( itbeg != itend )
        {
            itbeg = temp.ReadAsRawByte( itbeg );
            out_palette.push_back( std::move(temp) );
        }

        return itbeg;
    }

    //Writes a palette to a container. Expects to have enough room to output everything!
    template<class _outit>
        inline _outit WriteRawPalette_RGB24_As_RGB24( _outit itwhere, 
                                                      rgb24palette_t::const_iterator itpalbeg,
                                                      rgb24palette_t::const_iterator itpalend )
    {
        //Write palette
        while( itpalbeg != itpalend )
        {
            //(*itwhere) = itpalbeg->red;
            //++itwhere;
            //(*itwhere) = itpalbeg->green;
            //++itwhere;
            //(*itwhere) = itpalbeg->blue;
            //++itwhere;
            itwhere = itpalbeg->WriteAsRawByte( itwhere );
            ++itpalbeg;
        }

        return itwhere;
    }

    /****************************************************************
        ReadRawPalette_RGBX32_As_RGB24
            Read a palette to a container. Must be divisible by 4 !
    ****************************************************************/
    template<class _init>
        inline _init ReadRawPalette_RGBX32_As_RGB24( _init            itbeg,
                                                     _init            itend,
                                                     rgb24palette_t & out_palette )
    {
        colorRGB24   temp;
        unsigned int size = std::distance( itbeg, itend );

        if( size % 4u != 0u  )
            throw std::length_error("ReadRawPalette_RGBX32_As_RGB24() : Nb of bytes to build palette from not divisible by 4 !");

        out_palette.reserve( size / 4u );
        out_palette.resize(0u);

        //Write palette
        while( itbeg != itend )
        {
            itbeg = temp.ReadAsRawByte( itbeg );
            ++itbeg; //Skip the ignored 0x80 byte
            out_palette.push_back( std::move(temp) );
        }

        return itbeg;
    }

    /****************************************************************
        WriteRawPalette_RGB24_As_RGBX32
            Writes a palette to a container. 
            Expects to have enough room to output everything!
    ****************************************************************/
    template<class _outit>
        inline _outit WriteRawPalette_RGB24_As_RGBX32( _outit itwhere, 
                                                       rgb24palette_t::const_iterator itpalbeg,
                                                       rgb24palette_t::const_iterator itpalend )
    {
        //Write palette
        while( itpalbeg != itpalend )
        {
            //(*itwhere) = itpalbeg->red;
            //++itwhere;
            //(*itwhere) = itpalbeg->green;
            //++itwhere;
            //(*itwhere) = itpalbeg->blue;
            //++itwhere;
            //(*itwhere) = RGBX_UNUSED_BYTE_VALUE;
            //++itwhere;

            //++itpalbeg;
            itwhere = itpalbeg->WriteAsRawByte( itwhere );
            ++itpalbeg;
        }

        return itwhere;
    }



//==================================================================
// Indexed Image Data to Non-Indexed Converters
//================================================================== 

    // #TODO: everything in there should go !!!

    /****************************************************************
        indexed_to_nonindexed
            Parent class for our two functor below.
    ****************************************************************/
    //class indexed_to_nonindexed
    //{
    //public:
    //    //The source palette to use on the range when called
    //    indexed_to_nonindexed( const std::vector<colorRGB24> & palette, std::vector<colorRGB24>::iterator & inout_itimg );

    //    virtual void operator()( uint8_t ) = 0;

    //protected:
    //    const std::vector<colorRGB24> &      _palette;
    //    std::vector<colorRGB24>::iterator & _itimg;
    //};

    ///****************************************************************
    //    indexedcolor_4bits_to_24bits_pixels
    //        A functor for converting palette indexes into a list of
    //        RGB24 colors.
    //        Works on 4bits palette indexes. 
    //****************************************************************/
    //class indexedcolor_4bits_to_24bits_pixels : public indexed_to_nonindexed
    //{
    //public:
    //    //using indexed_to_nonindexed::indexed_to_nonindexed; //C++ 11 constructor inheritance, sadly, msvc don't know about those
    //    indexedcolor_4bits_to_24bits_pixels( const std::vector<colorRGB24> & palette, std::vector<colorRGB24>::iterator & inout_itimg );

    //    void operator()( uint8_t pixels );
    //};

    ///****************************************************************
    //    indexedcolor_8bits_to_24bits_pixels
    //        A functor for converting palette indexes into a list of
    //        RGB24 colors.
    //        Works on 8bits palette indexes. 
    //****************************************************************/
    //class indexedcolor_8bits_to_24bits_pixels : public indexed_to_nonindexed
    //{
    //public:
    //    //using indexed_to_nonindexed::indexed_to_nonindexed; //C++ 11 constructor inheritance, sadly, msvc don't know about those
    //    indexedcolor_8bits_to_24bits_pixels( const std::vector<colorRGB24> & palette, std::vector<colorRGB24>::iterator & inout_itimg );

    //    void operator()( uint8_t pixel );
    //};

};};

#endif